/**
 * Copyright 2010 The Apache Software Foundation
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.hadoop.hbase.ipc;

import java.io.IOException;
import java.net.ConnectException;
import java.util.List;

import org.apache.hadoop.hbase.Abortable;
import org.apache.hadoop.hbase.HRegionInfo;
//import org.apache.hadoop.hbase.HServerInfo;
import org.apache.hadoop.hbase.NotServingRegionException;
import org.apache.hadoop.hbase.Stoppable;
import org.apache.hadoop.hbase.client.Append;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Increment;
import org.apache.hadoop.hbase.client.MultiAction;
import org.apache.hadoop.hbase.client.MultiResponse;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.RowMutations;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.coprocessor.Exec;
import org.apache.hadoop.hbase.client.coprocessor.ExecResult;
import org.apache.hadoop.hbase.filter.WritableByteArrayComparable;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.io.hfile.BlockCacheColumnFamilySummary;
import org.apache.hadoop.hbase.ipc.VersionedProtocol;
import org.apache.hadoop.hbase.regionserver.RegionOpeningState;
import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
//import org.apache.hadoop.hbase.regionserver.wal.HLog;
import org.apache.hadoop.hbase.security.KerberosInfo;
import org.apache.hadoop.hbase.security.TokenInfo;
import org.apache.hadoop.hbase.util.Pair;

/**
 * Clients interact with HRegionServers using a handle to the HRegionInterface.
 *
 * <p>NOTE: if you change the interface, you must change the RPC version
 * number in HBaseRPCProtocolVersion
 */
@KerberosInfo(serverPrincipal = "hbase.regionserver.kerberos.principal")
@TokenInfo("HBASE_AUTH_TOKEN")
public interface HRegionInterface extends VersionedProtocol, Stoppable, Abortable {
	/**
	 * This Interfaces' version. Version changes when the Interface changes.
	 */
	// All HBase Interfaces used derive from HBaseRPCProtocolVersion.  It
	// maintained a single global version number on all HBase Interfaces.  This
	// meant all HBase RPC was broke though only one of the three RPC Interfaces
	// had changed.  This has since been undone.
	public static final long VERSION = 29L;

	/**
	 * Get metainfo about an HRegion
	 *
	 * @param regionName name of the region
	 * @return HRegionInfo object for region
	 * @throws NotServingRegionException
	 * @throws ConnectException
	 * @throws IOException This can manifest as an Hadoop ipc {@link RemoteException}
	 */
	public HRegionInfo getRegionInfo(final byte[] regionName) throws NotServingRegionException, ConnectException, IOException;

	/**
	 * Flush the given region
	 * @param region name
	 */
	public void flushRegion(byte[] regionName) throws IllegalArgumentException, IOException;

	/**
	 * Flush the given region if lastFlushTime < ifOlderThanTS
	 * @param region name
	 * @param timestamp
	 */
	public void flushRegion(byte[] regionName, long ifOlderThanTS) throws IllegalArgumentException, IOException;

	/**
	 * Gets last flush time for the given region
	 * @return the last flush time for a region
	 */
	public long getLastFlushTime(byte[] regionName);

	/**
	 * Get a list of store files for a particular CF in a particular region
	 * @param region name
	 * @param CF name
	 * @return the list of store files
	 */
	public List<String> getStoreFileList(byte[] regionName, byte[] columnFamily) throws IllegalArgumentException;

	/**
	 * Get a list of store files for a set of CFs in a particular region
	 * @param region name
	 * @param CF names
	 * @return the list of store files
	 */
	public List<String> getStoreFileList(byte[] regionName, byte[][] columnFamilies) throws IllegalArgumentException;

	/**
	 * Get a list of store files for all CFs in a particular region
	 * @param region name
	 * @return the list of store files
	 */
	public List<String> getStoreFileList(byte[] regionName) throws IllegalArgumentException;

	/**
	 * Return all the data for the row that matches <i>row</i> exactly,
	 * or the one that immediately preceeds it.
	 *
	 * @param regionName region name
	 * @param row row key
	 * @param family Column family to look for row in.
	 * @return map of values
	 * @throws IOException e
	 */
	public Result getClosestRowBefore(final byte[] regionName, final byte[] row, final byte[] family) throws IOException;

	/**
	 * Perform Get operation.
	 * @param regionName name of region to get from
	 * @param get Get operation
	 * @return Result
	 * @throws IOException e
	 */
	public Result get(byte[] regionName, Get get) throws IOException;

	/**
	 * Perform exists operation.
	 * @param regionName name of region to get from
	 * @param get Get operation describing cell to test
	 * @return true if exists
	 * @throws IOException e
	 */
	public boolean exists(byte[] regionName, Get get) throws IOException;

	/**
	 * Put data into the specified region
	 * @param regionName region name
	 * @param put the data to be put
	 * @throws IOException e
	 */
	public void put(final byte[] regionName, final Put put) throws IOException;

	/**
	 * Put an array of puts into the specified region
	 *
	 * @param regionName region name
	 * @param puts List of puts to execute
	 * @return The number of processed put's.  Returns -1 if all Puts
	 * processed successfully.
	 * @throws IOException e
	 */
	public int put(final byte[] regionName, final List<Put> puts) throws IOException;

	/**
	 * Deletes all the KeyValues that match those found in the Delete object,
	 * if their ts <= to the Delete. In case of a delete with a specific ts it
	 * only deletes that specific KeyValue.
	 * @param regionName region name
	 * @param delete delete object
	 * @throws IOException e
	 */
	public void delete(final byte[] regionName, final Delete delete) throws IOException;

	/**
	 * Put an array of deletes into the specified region
	 *
	 * @param regionName region name
	 * @param deletes delete List to execute
	 * @return The number of processed deletes.  Returns -1 if all Deletes
	 * processed successfully.
	 * @throws IOException e
	 */
	public int delete(final byte[] regionName, final List<Delete> deletes) throws IOException;

	/**
	 * Atomically checks if a row/family/qualifier value match the expectedValue.
	 * If it does, it adds the put. If passed expected value is null, then the
	 * check is for non-existance of the row/column.
	 *
	 * @param regionName region name
	 * @param row row to check
	 * @param family column family
	 * @param qualifier column qualifier
	 * @param value the expected value
	 * @param put data to put if check succeeds
	 * @throws IOException e
	 * @return true if the new put was execute, false otherwise
	 */
	public boolean checkAndPut(final byte[] regionName, final byte[] row, final byte[] family, final byte[] qualifier,
			final byte[] value, final Put put) throws IOException;

	/**
	 * Atomically checks if a row/family/qualifier value match the expectedValue.
	 * If it does, it adds the delete. If passed expected value is null, then the
	 * check is for non-existance of the row/column.
	 *
	 * @param regionName region name
	 * @param row row to check
	 * @param family column family
	 * @param qualifier column qualifier
	 * @param value the expected value
	 * @param delete data to delete if check succeeds
	 * @throws IOException e
	 * @return true if the new delete was execute, false otherwise
	 */
	public boolean checkAndDelete(final byte[] regionName, final byte[] row, final byte[] family, final byte[] qualifier,
			final byte[] value, final Delete delete) throws IOException;

	/**
	 * Atomically increments a column value. If the column value isn't long-like,
	 * this could throw an exception. If passed expected value is null, then the
	 * check is for non-existance of the row/column.
	 *
	 * @param regionName region name
	 * @param row row to check
	 * @param family column family
	 * @param qualifier column qualifier
	 * @param amount long amount to increment
	 * @param writeToWAL whether to write the increment to the WAL
	 * @return new incremented column value
	 * @throws IOException e
	 */
	public long incrementColumnValue(byte[] regionName, byte[] row, byte[] family, byte[] qualifier, long amount,
			boolean writeToWAL) throws IOException;

	public void mutateRow(byte[] regionName, RowMutations rm) throws IOException;

	/**
	 * Appends values to one or more columns values in a row. Optionally
	 * Returns the updated keys after the append.
	 * <p>
	 * This operation does not appear atomic to readers. Appends are done
	 * under a row lock but readers do not take row locks.
	 * @param regionName region name
	 * @param append Append operation
	 * @return changed cells (maybe null)
	 */
	public Result append(byte[] regionName, Append append) throws IOException;

	/**
	 * Increments one or more columns values in a row.  Returns the
	 * updated keys after the increment.
	 * <p>
	 * This operation does not appear atomic to readers.  Increments are done
	 * under a row lock but readers do not take row locks.
	 * @param regionName region name
	 * @param increment increment operation
	 * @return incremented cells
	 */
	public Result increment(byte[] regionName, Increment increment) throws IOException;

	//
	// remote scanner interface
	//

	/**
	 * Opens a remote scanner with a RowFilter.
	 *
	 * @param regionName name of region to scan
	 * @param scan configured scan object
	 * @return scannerId scanner identifier used in other calls
	 * @throws IOException e
	 */
	public long openScanner(final byte[] regionName, final Scan scan) throws IOException;

	/**
	 * Get the next set of values
	 * @param scannerId clientId passed to openScanner
	 * @return map of values; returns null if no results.
	 * @throws IOException e
	 */
	public Result next(long scannerId) throws IOException;

	/**
	 * Get the next set of values
	 * @param scannerId clientId passed to openScanner
	 * @param numberOfRows the number of rows to fetch
	 * @return Array of Results (map of values); array is empty if done with this
	 * region and null if we are NOT to go to the next region (happens when a
	 * filter rules that the scan is done).
	 * @throws IOException e
	 */
	public Result[] next(long scannerId, int numberOfRows) throws IOException;

	/**
	 * Close a scanner
	 *
	 * @param scannerId the scanner id returned by openScanner
	 * @throws IOException e
	 */
	public void close(long scannerId) throws IOException;

	/**
	 * Opens a remote row lock.
	 *
	 * @param regionName name of region
	 * @param row row to lock
	 * @return lockId lock identifier
	 * @throws IOException e
	 */
	public long lockRow(final byte[] regionName, final byte[] row) throws IOException;

	/**
	 * Releases a remote row lock.
	 *
	 * @param regionName region name
	 * @param lockId the lock id returned by lockRow
	 * @throws IOException e
	 */
	public void unlockRow(final byte[] regionName, final long lockId) throws IOException;

	/**
	 * @return All regions online on this region server
	 * @throws IOException e
	 */
	public List<HRegionInfo> getOnlineRegions() throws IOException;

	/**
	 * Method used when a master is taking the place of another failed one.
	 * @return This servers {@link HServerInfo}; it has RegionServer POV on the
	 * hostname which may not agree w/ how the Master sees this server.
	 * @throws IOException e
	 * @deprecated
	 */
	//public HServerInfo getHServerInfo() throws IOException;

	/**
	 * Method used for doing multiple actions(Deletes, Gets and Puts) in one call
	 * @param multi
	 * @return MultiResult
	 * @throws IOException
	 */
	public <R> MultiResponse multi(MultiAction<R> multi) throws IOException;

	/**
	 * Atomically bulk load multiple HFiles (say from different column families)
	 * into an open region.
	 * 
	 * @param familyPaths List of (family, hfile path) pairs
	 * @param regionName name of region to load hfiles into
	 * @return true if successful, false if failed recoverably
	 * @throws IOException if fails unrecoverably
	 */
	public boolean bulkLoadHFiles(List<Pair<byte[], String>> familyPaths, byte[] regionName) throws IOException;

	// Master methods

	/**
	 * Opens the specified region.
	 * 
	 * @param region
	 *          region to open
	 * @return RegionOpeningState 
	 *         OPENED         - if region open request was successful.
	 *         ALREADY_OPENED - if the region was already opened. 
	 *         FAILED_OPENING - if region opening failed.
	 *
	 * @throws IOException
	 */
	public RegionOpeningState openRegion(final HRegionInfo region) throws IOException;

	/**
	 * Opens the specified region.
	 * @param region
	 *          region to open
	 * @param versionOfOfflineNode
	 *          the version of znode to compare when RS transitions the znode from
	 *          OFFLINE state.
	 * @return RegionOpeningState 
	 *         OPENED         - if region open request was successful.
	 *         ALREADY_OPENED - if the region was already opened. 
	 *         FAILED_OPENING - if region opening failed.
	 * @throws IOException
	 */
	public RegionOpeningState openRegion(HRegionInfo region, int versionOfOfflineNode) throws IOException;

	/**
	 * Opens the specified regions.
	 * @param regions regions to open
	 * @throws IOException
	 */
	public void openRegions(final List<HRegionInfo> regions) throws IOException;

	/**
	 * Closes the specified region.
	 * @param region region to close
	 * @return true if closing region, false if not
	 * @throws IOException
	 */
	public boolean closeRegion(final HRegionInfo region) throws IOException;

	/**
	 * Closes the specified region.
	 * @param region region to close
	 * @param versionOfClosingNode
	 *          the version of znode to compare when RS transitions the znode
	 *          from CLOSING state.
	 * @return true if closing region, false if not
	 * @throws IOException
	 */
	public boolean closeRegion(final HRegionInfo region, final int versionOfClosingNode) throws IOException;

	/**
	 * Closes the specified region and will use or not use ZK during the close
	 * according to the specified flag.
	 * @param region region to close
	 * @param zk true if transitions should be done in ZK, false if not
	 * @return true if closing region, false if not
	 * @throws IOException
	 */
	public boolean closeRegion(final HRegionInfo region, final boolean zk) throws IOException;

	/**
	 * Closes the region in the RS with the specified encoded regionName and will
	 * use or not use ZK during the close according to the specified flag. Note
	 * that the encoded region name is in byte format.
	 * 
	 * @param encodedRegionName
	 *          in bytes
	 * @param zk
	 *          true if to use zookeeper, false if need not.
	 * @return true if region is closed, false if not.
	 * @throws IOException
	 */
	public boolean closeRegion(byte[] encodedRegionName, final boolean zk) throws IOException;

	// Region administrative methods

	/**
	 * Flushes the MemStore of the specified region.
	 * <p>
	 * This method is synchronous.
	 * @param regionInfo region to flush
	 * @throws NotServingRegionException
	 * @throws IOException
	 * @deprecated use {@link #flushRegion(byte[])} instead
	 */
	void flushRegion(HRegionInfo regionInfo) throws NotServingRegionException, IOException;

	/**
	 * Splits the specified region.
	 * <p>
	 * This method currently flushes the region and then forces a compaction which
	 * will then trigger a split.  The flush is done synchronously but the
	 * compaction is asynchronous.
	 * @param regionInfo region to split
	 * @throws NotServingRegionException
	 * @throws IOException
	 */
	void splitRegion(HRegionInfo regionInfo) throws NotServingRegionException, IOException;

	/**
	 * Splits the specified region.
	 * <p>
	 * This method currently flushes the region and then forces a compaction which
	 * will then trigger a split.  The flush is done synchronously but the
	 * compaction is asynchronous.
	 * @param regionInfo region to split
	 * @param splitPoint the explicit row to split on
	 * @throws NotServingRegionException
	 * @throws IOException
	 */
	void splitRegion(HRegionInfo regionInfo, byte[] splitPoint) throws NotServingRegionException, IOException;

	/**
	 * Compacts the specified region.  Performs a major compaction if specified.
	 * <p>
	 * This method is asynchronous.
	 * @param regionInfo region to compact
	 * @param major true to force major compaction
	 * @throws NotServingRegionException
	 * @throws IOException
	 */
	void compactRegion(HRegionInfo regionInfo, boolean major) throws NotServingRegionException, IOException;

	/**
	 * Replicates the given entries. The guarantee is that the given entries
	 * will be durable on the slave cluster if this method returns without
	 * any exception.
	 * hbase.replication has to be set to true for this to work.
	 *
	 * @param entries entries to replicate
	 * @throws IOException
	 */
	//public void replicateLogEntries(HLog.Entry[] entries) throws IOException;

	/**
	 * Executes a single {@link org.apache.hadoop.hbase.ipc.CoprocessorProtocol}
	 * method using the registered protocol handlers.
	 * {@link CoprocessorProtocol} implementations must be registered via the
	 * {@link org.apache.hadoop.hbase.regionserver.HRegion#registerProtocol(Class, org.apache.hadoop.hbase.ipc.CoprocessorProtocol)}
	 * method before they are available.
	 *
	 * @param regionName name of the region against which the invocation is executed
	 * @param call an {@code Exec} instance identifying the protocol, method name,
	 *     and parameters for the method invocation
	 * @return an {@code ExecResult} instance containing the region name of the
	 *     invocation and the return value
	 * @throws IOException if no registered protocol handler is found or an error
	 *     occurs during the invocation
	 * @see org.apache.hadoop.hbase.regionserver.HRegion#registerProtocol(Class, org.apache.hadoop.hbase.ipc.CoprocessorProtocol)
	 */
	ExecResult execCoprocessor(byte[] regionName, Exec call) throws IOException;

	/**
	 * Atomically checks if a row/family/qualifier value match the expectedValue.
	 * If it does, it adds the put. If passed expected value is null, then the
	 * check is for non-existance of the row/column.
	 *
	 * @param regionName
	 * @param row
	 * @param family
	 * @param qualifier
	 * @param compareOp
	 * @param comparator
	 * @param put
	 * @throws IOException
	 * @return true if the new put was execute, false otherwise
	 */
	public boolean checkAndPut(final byte[] regionName, final byte[] row, final byte[] family, final byte[] qualifier,
			final CompareOp compareOp, final WritableByteArrayComparable comparator, final Put put) throws IOException;

	/**
	 * Atomically checks if a row/family/qualifier value match the expectedValue.
	 * If it does, it adds the delete. If passed expected value is null, then the
	 * check is for non-existance of the row/column.
	 *
	 * @param regionName
	 * @param row
	 * @param family
	 * @param qualifier
	 * @param compareOp
	 * @param comparator
	 * @param delete
	 * @throws IOException
	 * @return true if the new put was execute, false otherwise
	 */
	public boolean checkAndDelete(final byte[] regionName, final byte[] row, final byte[] family, final byte[] qualifier,
			final CompareOp compareOp, final WritableByteArrayComparable comparator, final Delete delete) throws IOException;

	/**
	 * Performs a BlockCache summary and returns a List of BlockCacheColumnFamilySummary objects.
	 * This method could be fairly heavyweight in that it evaluates the entire HBase file-system
	 * against what is in the RegionServer BlockCache. 
	 * 
	 * @return BlockCacheColumnFamilySummary
	 * @throws IOException exception
	 */
	public List<BlockCacheColumnFamilySummary> getBlockCacheColumnFamilySummaries() throws IOException;

	/**
	 * Roll the log writer. That is, start writing log messages to a new file.
	 * 
	 * @throws IOException
	 * @throws FailedLogCloseException
	 * @return If lots of logs, flush the returned regions so next time through
	 * we can clean logs. Returns null if nothing to flush.  Names are actual
	 * region names as returned by {@link HRegionInfo#getEncodedName()} 
	 */
	public byte[][] rollHLogWriter() throws IOException, FailedLogCloseException;

	@Override
	public void stop(String why);
}
